use super::vec3::Vec3;
use std::fmt;
use std::ops::{ Neg, Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign };


#[repr(C)]
#[derive(Copy, Clone)]
pub struct Vec2(pub f32, pub f32);

impl Vec2 {
    pub fn from_tuple((x, y): (f32, f32)) -> Self {
        Self(x, y)
    }

    pub fn right() -> Self {
        Self(1.0, 0.0)
    }

    pub fn left() -> Self {
        Self(-1.0, 0.0)
    }

    pub fn up() -> Self {
        Self(0.0, 1.0)
    }

    pub fn down() -> Self {
        Self(0.0, -1.0)
    }

    pub fn zero() -> Self {
        Self(0.0, 0.0)
    }

    pub fn dot(self, rhs: Self) -> f32 {
        self.0 * rhs.0 + self.1 * rhs.1
    }

    pub fn tuple(&self) -> (f32, f32) {
        (self.0, self.1)
    }

    pub fn to_array(&self) -> [f32; 2] {
        [self.0, self.1]
    }

    pub fn set(&mut self, x: f32, y: f32) {
        self.0 = x;
        self.1 = y;
    }

    pub fn cross(&mut self, rhs: Self) -> f32 {
        self.0 * rhs.1 - self.1 * rhs.0
    }
}

impl Add for Vec2 {
    type Output = Self;
    fn add(self, rhs: Self) -> Self {
        Self(self.0 + rhs.0, self.1 + rhs.1)
    }
}

impl AddAssign for Vec2 {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
        self.1 += rhs.1;
    }
}

impl Sub for Vec2 {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self {
        Self(self.0 - rhs.0, self.1 - rhs.1)
    }
}

impl SubAssign for Vec2 {
    fn sub_assign(&mut self, rhs: Self) {
        self.0 -= rhs.0;
        self.1 -= rhs.1;
    }
}

impl Mul<f32> for Vec2 {
    type Output = Self;
    fn mul(self, rhs: f32) -> Self::Output {
        Self(self.0 * rhs, self.1 * rhs)
    }
}

impl MulAssign<f32> for Vec2 {
    fn mul_assign(&mut self, rhs: f32) {
        self.0 *= rhs;
        self.1 *= rhs;
    }
}

impl Mul<Vec2> for f32 {
    type Output = Vec2;
    fn mul(self, rhs: Vec2) -> Self::Output {
        Vec2(self * rhs.0, self * rhs.1)
    }
}

impl Div for Vec2 {
    type Output = Self;
    fn div(self, rhs: Self) -> Self {
        Vec2(self.0 / rhs.0, self.1 / rhs.1)
    }
}

impl DivAssign for Vec2 {
    fn div_assign(&mut self, rhs: Self) {
        self.0 /= rhs.0;
        self.1 /= rhs.1;
    }
}

impl Neg for Vec2 {
    type Output = Self;
    fn neg(self) -> Self {
        Self(-self.0, -self.1)
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct IVec2(pub i32, pub i32);

impl IVec2 {
    pub fn right() -> Self {
        Self(1, 0)
    }

    pub fn left() -> Self {
        Self(-1, 0)
    }

    pub fn up() -> Self {
        Self(0, 1)
    }

    pub fn down() -> Self {
        Self(0, -1)
    }

    pub fn dot(self, rhs: Self) -> i32 {
        self.0 * rhs.0 + self.1 * self.1
    }
}

impl Neg for IVec2 {
    type Output = Self;
    fn neg(self) -> Self {
        Self(-self.0, -self.1)
    }
}

impl Add for IVec2 {
    type Output = Self;
    fn add(self, rhs: Self) -> Self {
        Self(self.0 + rhs.0, self.1 + rhs.1)
    }
}

impl AddAssign for IVec2 {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
        self.1 += rhs.1;
    }
}

impl Sub for IVec2 {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self {
        Self(self.0 - rhs.0, self.1 - rhs.1)
    }
}

impl SubAssign for IVec2 {
    fn sub_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
        self.1 += rhs.1;
    }
}

impl Mul for IVec2 {
    type Output = Self;
    fn mul(self, rhs: Self) -> Self {
        Self(self.0 * rhs.0, self.1 * rhs.0)
    }
}

impl MulAssign for IVec2 {
    fn mul_assign(&mut self, rhs: Self) {
        self.0 *= rhs.0;
        self.1 *= rhs.1;
    }
}

impl Div for IVec2 {
    type Output = Self;
    fn div(self, rhs: Self) -> Self {
        Self(self.0 / rhs.0, self.1 / rhs.1)
    }
}

impl DivAssign for IVec2 {
    fn div_assign(&mut self, rhs: Self) {
        self.0 /= rhs.0;
        self.1 /= rhs.1;
    }
}

impl fmt::Display for Vec2 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[{}, {}]", self.0, self.1)
    }
}

impl Into<Vec3> for Vec2 {
    fn into(self) -> Vec3 {
        Vec3(self.0, self.1, 0.0)
    }
}

impl Default for Vec2 {
    fn default() -> Self {
        Self::zero()
    }
}